#include "MyUart.h"

//串口测试程序   V1.1           修改日期：2025/6/6
//实现功能：单函数控制打印单个字符
//					单函数控制打印字符串
//					可变重定向（修改Printf_flag ，即可改变重定向输出串口）
//					usart0可以使用循环DMA接收定长数据（V1.1 new）
//By:TAO XIN YU (陶鑫宇)


//printf重定向标志位 0:使用UART0    1：使用UART1 
unsigned char Printf_flag=0;

//串口0接收缓存数组
char receiver_0_buffer[receiver_0_buffer_size]={0};

//串口1接收缓存数组
char receiver_1_buffer[receiver_1_buffer_size]={0};

//串口初始化(全部)
//无参
//无返回值
void MyUart_all_init(void)
{
	MyUart0_init();
	MyUart1_init();
}

//串口0初始化
//无参
//无返回值
void MyUart0_init(void)
{
	//使能串口GPIO时钟
	rcu_periph_clock_enable(Uart0_RCU_GPIOX);
	//使能UART0外设时钟
	rcu_periph_clock_enable(RCU_USART0);
	
	//配置串口GPIO复用 TX
	gpio_af_set(Uart0_GPIOX,GPIO_AF_1,Uart0_TX_pin);
	//配置串口GPIO复用 RX
	gpio_af_set(Uart0_GPIOX,GPIO_AF_1,Uart0_RX_pin);
	
	//配置串口GPIO电气属性 复用上拉 TX
	gpio_mode_set(Uart0_GPIOX,GPIO_MODE_AF,GPIO_PUPD_PULLUP,Uart0_TX_pin);
	//配置串口GPIO输出配置 推挽输出 50Mhz TX
	gpio_output_options_set(Uart0_GPIOX,GPIO_OTYPE_PP,GPIO_OSPEED_50MHZ,Uart0_TX_pin);
	
	//配置串口GPIO电气属性 复用上拉 RX
	gpio_mode_set(Uart0_GPIOX,GPIO_MODE_AF,GPIO_PUPD_PULLUP,Uart0_RX_pin);
	//配置串口GPIO输出配置 推挽输出 50Mhz TX
	gpio_output_options_set(Uart0_GPIOX,GPIO_OTYPE_PP,GPIO_OSPEED_50MHZ,Uart0_RX_pin);
	
	//配置Uart0
	//配置前恢复Uart0缺省配置，防止出现意外错误
	usart_deinit(USART0);
	//配置数据位 8位
	usart_word_length_set(USART0,Uart0_word);
	//配置停止位 1位
	usart_stop_bit_set(USART0,Uart0_stop);
	//配置奇偶校验 无校验
	usart_parity_config(USART0,Uart0_parity);
	//配置baudrate(波特率)
	usart_baudrate_set(USART0,115200);
	//使能接收
	usart_receive_config(USART0,USART_RECEIVE_ENABLE);
	//使能发送
	usart_transmit_config(USART0,USART_TRANSMIT_ENABLE);

//是否使用DMA 接收
//不使用DMA
#if UART0_DMA_RX_ENABLE == 0
	//UART0中断控制 分配优先级
	nvic_irq_enable(USART0_IRQn,Uart0_nvic_priority);
	//使能UART0接收缓存区非空中断
	usart_interrupt_enable(USART0,USART_INT_RBNE);


#else //使用DMA
	
	//开启DMA时钟
	rcu_periph_clock_enable(RCU_DMA);
	
	//定义DMA初始化结构体
	dma_parameter_struct  mydma_init_structure={0};
	//恢复DMA通道2缺省配置，避免未知错误
	dma_deinit(DMA_CH2);
	//恢复DMA结构体缺省配置，避免未知错误
	dma_struct_para_init(&mydma_init_structure);
	//DMA方向 从外设到存储器
	mydma_init_structure.direction=DMA_PERIPHERAL_TO_MEMORY;
	//设置DMA 存储器基地址
	mydma_init_structure.memory_addr=(unsigned int)receiver_0_buffer;
	//设置DMA 存储器地址自增
	mydma_init_structure.memory_inc=DMA_MEMORY_INCREASE_ENABLE;
	//设置DMA 存储器数据位宽 8位
	mydma_init_structure.memory_width=DMA_MEMORY_WIDTH_8BIT;
	//设置DMA 存储器数据位深，容量
	mydma_init_structure.number=receiver_0_buffer_size;
	//设置DMA 外设基地址
	mydma_init_structure.periph_addr=UART0_RXDATA_ADDR;
	//设置DMA 外设地址不自增
	mydma_init_structure.periph_inc=DMA_PERIPH_INCREASE_DISABLE;
	//设置DMA 外设数据位宽 8位
	mydma_init_structure.periph_width=DMA_PERIPHERAL_WIDTH_8BIT;
	//设置DMA 通道优先级  最优先
	mydma_init_structure.priority=DMA_PRIORITY_ULTRA_HIGH;
	//DMA初始化
	dma_init(DMA_CH2,&mydma_init_structure);
	
	//DMA模式控制
	//使能DMA循环
	dma_circulation_enable(DMA_CH2);
	//关闭DMA 存储器到存储器通道
	dma_memory_to_memory_disable(DMA_CH2);
	
	//开启串口0 DMA外设接收
	usart_dma_receive_config(USART0,USART_DENR_ENABLE);
	//使能DMA满载接收完成中断
	dma_interrupt_enable(DMA_CH2,DMA_INT_FTF);
	//DMA通道使能
	dma_channel_enable(DMA_CH2);
	
	//DMA通道NVIC配置使能	
	nvic_irq_enable(DMA_Channel1_2_IRQn,0);
	
#endif


	//使能外设
	usart_enable(USART0);
}

//是否使用DMA 接收
//不使用DMA
#if UART0_DMA_RX_ENABLE == 0
//串口0中断
void USART0_IRQHandler(void)
{
	unsigned char rx_1_temp=0;
	static unsigned char rx_1_state=0;
	static unsigned char rx_1_count=0;
	//如果是 读取缓存非空中断
	if(RESET != usart_interrupt_flag_get(USART0,USART_INT_FLAG_RBNE))
	{
		//接收单个字符
		rx_1_temp = usart_data_receive(USART0);
		//如果还没开始接收数据包，包头固定为  ‘@’
		if(rx_1_state == 0)
		{
			//如果读取到了包头
			if((unsigned char)'@' == rx_1_temp)
			{
				//进入接收数据包流程
				rx_1_state=1;
			}		
		}
		//如果是接受数据包流程
		else if(rx_1_state == 1)
		{
			//判断是否为包尾
			if((unsigned char)'\r' == rx_1_temp || rx_1_count==receiver_0_buffer_size-1)
			{
				//如果是包尾，就进入封包流程
				rx_1_state=2;		
			}
			//如果不是包尾，继续接收数据
			else
			{
				//存入数据
				receiver_0_buffer[rx_1_count]=rx_1_temp;
				//数组索引加1
				rx_1_count++;			
			}
		}
		//封包流程
		else if(rx_1_state == 2)
		{
			//在数据尾部添加\0，为了printf正确输出   遇到\0停止打印
			receiver_0_buffer[rx_1_count]='\0';
			//清除接收计数
			rx_1_count=0;
			//清除接收状态
			rx_1_state=0;
			//设置串口重定向位置为UASRT0
			Printf_flag=0;
			printf("data:%s\r\n",receiver_0_buffer);
		}
	}
}

#else	 //使用DMA
//DMA通道1、2中断
void DMA_Channel1_2_IRQHandler(void)
{
	//如果是DMA_CH2 接收满载中断
	if(RESET != dma_interrupt_flag_get(DMA_CH2,DMA_INT_FLAG_FTF))
	{
		//重定向到USART0
		Printf_flag=0;
		//输出接收到的数据
		printf("dma_data:%s",receiver_0_buffer);
		//清除DMA_CH2所有中断标志
		dma_interrupt_flag_clear(DMA_CH2,DMA_INT_FLAG_G);
	}
}



#endif

//串口1中断
void USART1_IRQHandler(void)
{
	unsigned char rx_2_temp=0;
	static unsigned char rx_2_state=0;
	static unsigned char rx_2_count=0;
	//如果是 读取缓存非空中断
	if(RESET != usart_interrupt_flag_get(USART1,USART_INT_FLAG_RBNE))
	{
		//接收单个字符
		rx_2_temp = usart_data_receive(USART1);
		//如果还没开始接收数据包，包头固定为  ‘@’
		if(rx_2_state == 0)
		{
			//如果读取到了包头
			if((unsigned char)'@' == rx_2_temp)
			{
				//进入接收数据包流程
				rx_2_state=1;
			}		
		}
		//如果是接受数据包流程
		else if(rx_2_state == 1)
		{
			//判断是否为包尾
			if((unsigned char)'\r' == rx_2_temp || rx_2_count==receiver_1_buffer_size-1)
			{
				//如果是包尾，就进入封包流程
				rx_2_state=2;		
			}
			//如果不是包尾，继续接收数据
			else
			{
				//存入数据
				receiver_1_buffer[rx_2_count]=rx_2_temp;
				//数组索引加1
				rx_2_count++;			
			}
		}
		//封包流程
		else if(rx_2_state == 2)
		{
			//在数据尾部添加\0，为了printf正确输出   遇到\0停止打印
			receiver_1_buffer[rx_2_count]='\0';
			//清除接收计数
			rx_2_count=0;
			//清除接收状态
			rx_2_state=0;
			//设置串口重定向位置为UASRT1
			Printf_flag=1;
			printf("data:%s\r\n",receiver_1_buffer);
		}
	}
}


//串口1初始化
//无参
//无返回值
void MyUart1_init(void)
{
	//使能串口GPIO时钟
	rcu_periph_clock_enable(Uart1_RCU_GPIOX);
	//使能UART0外设时钟
	rcu_periph_clock_enable(RCU_USART1);
	
	//配置串口GPIO复用 TX
	gpio_af_set(Uart1_GPIOX,GPIO_AF_1,Uart1_TX_pin);
	//配置串口GPIO复用 RX
	gpio_af_set(Uart1_GPIOX,GPIO_AF_1,Uart1_RX_pin);
	
	//配置串口GPIO电气属性 复用上拉 TX
	gpio_mode_set(Uart1_GPIOX,GPIO_MODE_AF,GPIO_PUPD_PULLUP,Uart1_TX_pin);
	//配置串口GPIO输出配置 推挽输出 50Mhz TX
	gpio_output_options_set(Uart1_GPIOX,GPIO_OTYPE_PP,GPIO_OSPEED_50MHZ,Uart1_TX_pin);
	
	//配置串口GPIO电气属性 复用上拉 RX
	gpio_mode_set(Uart1_GPIOX,GPIO_MODE_AF,GPIO_PUPD_PULLUP,Uart1_RX_pin);
	//配置串口GPIO输出配置 推挽输出 50Mhz TX
	gpio_output_options_set(Uart1_GPIOX,GPIO_OTYPE_PP,GPIO_OSPEED_50MHZ,Uart1_RX_pin);
	
	//配置Uart1
	//配置前恢复Uart1缺省配置，防止出现意外错误
	usart_deinit(USART1);
	//配置数据位 8位
	usart_word_length_set(USART1,Uart1_word);
	//配置停止位 1位
	usart_stop_bit_set(USART1,Uart1_stop);
	//配置奇偶校验 无校验
	usart_parity_config(USART1,Uart1_parity);
	//配置baudrate(波特率)
	usart_baudrate_set(USART1,115200);
	//使能接收
	usart_receive_config(USART1,USART_RECEIVE_ENABLE);
	//使能发送
	usart_transmit_config(USART1,USART_TRANSMIT_ENABLE);
	
	//UART1中断控制 分配优先级
	nvic_irq_enable(USART1_IRQn,Uart1_nvic_priority);
	//使能UART1接收缓存区非空中断
	usart_interrupt_enable(USART1,USART_INT_RBNE);
	//使能外设
	usart_enable(USART1);
}


//printf重定向函数
//Printf_flag：   0:使用UART0    1：使用UART1 
int fputc(int ch,FILE* f)
{
	if(Printf_flag == 0)
	{
		//串口0发送数据
		usart_data_transmit(USART0,(uint8_t)ch);
		//等待发送完成标志位
		while(RESET == usart_flag_get(USART0,USART_FLAG_TBE));
		return ch;
	};
	//串口1发送数据
	usart_data_transmit(USART1,(uint8_t)ch);
	//等待发送完成标志位
	while(RESET == usart_flag_get(USART1,USART_FLAG_TBE));
	return ch;	
}


//串口发送单个数据
//参数：UART_id             0:使用串口0      1:使用串口0
//参数：data                要发送的数据
//无返回值
void MyUART_send_one_data(unsigned char UART_id,char data)
{
	if(UART_id == 0)
	{
		//发送单个数据
		usart_data_transmit(USART0,(unsigned int)data);
		//等待发送完成标志位
		while(RESET == usart_flag_get(USART0,USART_FLAG_TBE));
	}
	else
	{
		//发送单个数据
		usart_data_transmit(USART1,(unsigned int)data);
		//等待发送完成标志位
		while(RESET == usart_flag_get(USART1,USART_FLAG_TBE));
	}
}

//串口发送字符串
//参数：UART_id             0:使用串口0      1:使用串口0
//参数：string              要发送的字符串
void MyUART_send_string(unsigned char UART_id,char* string)
{
	//发送计数，字符串索引
	unsigned int count=0;
	//循环发送数据，直到读取到'\0' ,即 字符串末尾
	while(string[count] != (unsigned char)'\0')
	{
		//发送单个数据
		MyUART_send_one_data(UART_id,string[count]);
		//索引加1
		count++;
	}
}

